Skip to content

skills(pm-dispatch): a REST request body travels through a file or a quoted heredoc, never an inline double-quoted string - #16701

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-16661-title-command-substitution
Sep 8, 2026
Merged

skills(pm-dispatch): a REST request body travels through a file or a quoted heredoc, never an inline double-quoted string#16701
os-zhuang merged 1 commit into
mainfrom
claude/issue-16661-title-command-substitution

Conversation

@os-musk

@os-musk os-musk commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16661

The filing path is confirmed, on the specimen and on six more. It is an agent's own REST create from Bash, not a workflow — so the routing stays domain:skills, and the repair lands on the instruction surface.

Routing and grading, quoted

Triage's routing (5575369751) wrote the re-judgement trigger:

📌 重判触发器,写死:一旦路径被定位——

  • 落在 .github/workflows/**scripts/ 的一般门禁面 ⇒ 改判 domain:devx;
  • 落在 .claude/** / skills/** / agent 指令面 ⇒ domain:skills(受管面 ⇒ 草稿 PR + 人工合并)。

The seat's grading (5576225752) fixed the shape:

Fix the quoting at the path, never the house style: no "avoid backticks in titles".

Measured below: no workflow and no scripts/** shell path in either repo puts an authored title through a shell. The trigger's second arm fires. domain:skills stands, and this is a draft on a governed surface.

The specimen: path confirmed

objectui#7562, filed by the App identity 2026-09-03T21:39:32Z. Two facts decide it.

1. The body kept its attribution footer. The instruction surface records the discriminator at platform-readings.md:348 / :350: MCP issue_write create drops the trailing attribution footer block on store; a REST POST /issues body keeps it. Read through the zero-quota page payload, #7562's stored body ends:

- #7561 — the operator-vocabulary fork, the other thing #6939's row does not reach

…followed, after a blank line, by a horizontal rule (three dashes) and then the bare attribution line — the italicised "Generated by Claude Code" link to claude.ai/code, with no session segment. ⚠️ That block is described rather than reproduced: the first version of this body quoted it literally inside the fence above, and the platform ate it on store — a footer-shaped block is stripped even from inside a code fence in a PR body. Recorded below.

Footer intact ⇒ created through REST, not through MCP. That alone removes the one write path the card had already excluded, and names the one that remains: curl from Bash.

2. The prescription existed at filing time, and says nothing about carriage. rest-channel.md's create row — ✓ 建卡带标签 POST .../issues … — landed 2026-08-24 (6d0cccc727), ten days before the specimen. It names the route and is silent on how the JSON travels. A session that builds that JSON inline in a double-quoted Bash string hands every backticked identifier in the title to the shell before the request exists; these containers run as root, which is what uid=0(root) reports.

Locating grep, re-run by this flight (objectstack 7797102139, objectui 34510e0c3 — both at their origin/main, a newer objectui tip than the seat's 0203a29e):

  • .github/workflows/** — every issue-creating step is github.rest.issues.create(...) inside actions/github-script (objectstack merge-queue-triage.yml:771, platform-checklist-watchdog.yml:358, test-nightly-tiers.yml:505; objectui shadcn-check.yml:215), or objectstack publish-smoke.yml:287 gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body "$body" whose title is assigned a literal string two lines above. No workflow interpolates an authored title into a shell. Neither repo uses anthropics/claude-code-action (zero files).
  • scripts/** — no issue-creating shell path in either repo.
  • .claude/** — no helper creates an issue. settings.json:30-40 allowlists curl -sS -X POST …/issues/*/labels and the GET reads, which is the corroborating structural fact: the fleet's sanctioned GitHub write channel is curl from Bash.

Verdict: agent-Bash-REST. premise_still_valid: true.

Population: six more specimens, and a false-positive class that had to be removed first

Exact-match channel, tried first and refused. search_issues, both repos, the literal phrase quoted:

q=repo:objectstack-ai/objectui  is:issue uid=0(root)      422  The search query contains invalid syntax.
q=repo:objectstack-ai/objectstack is:issue uid=0(root)    422  The search query contains invalid syntax.
q=repo:objectstack-ai/objectui  is:issue gid=0(root)      422  The search query contains invalid syntax.

The tool strips the quotes before sending, so exact-phrase matching never reaches the wire. A paren-free semantic query (issue title contains shell output uid=0 gid=0 groups=0 root command substitution, objectui) returned total_count: 0 with incomplete_results: falsenot a reading: the same-session control word (FilterBuilderConditionSchema omits id which the component reads at four sites) returned total_count: 8 with #7562 first, so the instrument is alive and simply cannot match a literal token inside a title.

Scripted fallback. list_issues, orderBy: CREATED_AT DESC, perPage: 100, fields: [number,title,created_at], since: 2026-09-01T00:00:00Z, paged to the window floor:

repo pages titles read created-window covered
objectui 8 750 2026-09-01T00:00Z → 2026-09-07T22:37Z
objectstack 10 1000 2026-09-01T00:51:46Z → 2026-09-07T22:32Z

Not exhaustive, and stated as such: the objectstack floor is 00:51:46Z, not midnight; the window is one week; and only a backticked token surrounded by separators leaves a visible hole, so the count is a lower bound by construction.

The false-positive class, and why it had to be removed. Twelve titles in that sweep carried a hole. Five of them were not corruption at all — they are intact in storage and mutilated by the MCP read path. Two independent extraction paths of the rendered page agree against the MCP read.

⚠️ The stored spellings below really carry angle-bracketed placeholders. They are written here as bare capitalised words, because the surface you are reading eats a tag-shaped fragment — which is the very class this table is about.

card MCP list_issues returned stored (page payload + rendered title tag)
objectstack#15180 i18n: spec still declares `objects. i18n: spec still declares `objects.OBJECT._views.VIEW.description` but objectui stopped resolving it …
objectui#8185 … only through an spelling`` … only through an `ANY` spelling
objectstack#16182 … `pkill -f ` is not covered … … `pkill -f PATTERN` is not covered …
objectstack#15971 … `OS_ALLOW_MAIN_EDITS=1 ` is refused … … `OS_ALLOW_MAIN_EDITS=1 CMD` is refused …
objectstack#14309 GET /api/v1/packages and /meta/package/ answer 500 placeholder-shaped, same class

objectui#8185's body carries no footer ⇒ it was created through MCP, and its stored title still holds the placeholder span ⇒ the MCP write path did not eat it. This is the platform-readings.md:218 / :220 class (「读侧并非一律可逆 … 被 MCP 读路径整个丢弃」), reaching titles; and it is why :349 had to go (see the folds below). Without this pass the population reading would have been 12, and five of them wrong.

The six that survive both channels. Each carries the hole in storage, each was created through REST (attribution footer intact), each is authored by the App identity. Recorded verbatim in fenced code so the double space survives rendering — ⛔ none repaired:

objectui#7798    2026-09-05T20:56:04Z  finding(docs): two doc blocks teach , a helper no package exports — and the #7652 gate cannot see it
objectui#7673    2026-09-04T19:14:36Z  finding(studio): the dashboard  zh overlay now translates a sentence the spec no longer says, and its citation comment quotes the retired string
objectui#7235    2026-09-01T19:47:02Z  sdui-parser (renderer/preview copy): a  attribute still overwrites the html-tier discriminator — objectstack now refuses it at parse
objectstack#15511 2026-09-04T19:15:50Z platform-objects(i18n): the zh-CN dashboard  helpText leaf goes stale the moment #14586 lands — a fresh instance of the class #14931 names, with every gate green
objectstack#15497 2026-09-04T18:31:17Z [finding] scripts/pm/os-regen-merge.sh admits a phantom  pathspec — its os-regen list is 18 entries where .gitattributes has 17 real patterns
objectstack#15026 2026-09-03T17:51:12Z finding(changeset): the pending  changeset still tells authors the lint accepts the canonical ListView spelling — the step-1 tightening makes every clause of that sentence false

One of them reconstructs exactly, which is what makes the mechanism decisive rather than suggestive: objectstack#15511's missing token is `gap` — its own siblings objectstack#14586 ("dashboard gap help text says …") and objectstack#16165 ("the es-ES and ja-JP dashboard gap helpText leaves …") spell it. gap is not a command, so the substitution printed nothing and consumed the backticks, leaving the double space. objectui#7673 ("the dashboard zh overlay") is the same token on the same subject. A write-side sanitizer that eats angle-bracket spans cannot eat `gap`. A shell can, and does.

7 specimens (the original plus 6) in 1,750 titles over one week, both repos. The mechanism is not rare and it is not confined to one session.

The change

Two files, three insertions, three deletions, both files net zero against their ratchet ceilings.

references/rest-channel.md 写侧, after the create row (+2):

- 请求体走文件(`-d @file`)或引号定界 heredoc(`<<'EOF'`),⛔ 永不内联双引号串。          98 bytes
- 双引号内 shell 先展开反引号、`$(...)`、`$VAR`,请求尚未成形;只标题坏而正文完好即此形。   118 bytes

references/platform-readings.md, after the REST-create row (+1):

- 内联双引号 JSON 建卡:标题反引号标识符被 shell 以 root 展开,正文完好 —— 内容被执行。    118 bytes

All three are under the 120-byte cap.

The folds, with their survivals

⚠️ The dispatch recorded rest-channel.md as having no ratchet row. Measured, it has one — ['.claude/skills/pm-dispatch/references/rest-channel.md', 82] in check-skill-line-ratchet.mjs:642 — and the file was at 82 lines, zero headroom. So both files are fold-paid, not just one. ⛔ No ceiling was raised.

rest-channel.md −2. Both deletions are the exact shape that table's own rule forbids — :67 「本表只指路,⛔ 不在两处各存一份」:

deleted survives at
- MCP list_issues 永不返回 assignees,这条差别本身就是走 REST 的理由。 platform-readings.md:213list_issues 永不返回 assignees:fields 枚举无此成员,不传也没有。」 — the fuller form, it names why. The "reason to go REST" half survives one line up at rest-channel.md:24, whose ✓ row already lists assignees as present on the REST read.
- 后者可瞬态 404 ⇒ PR 文件读取优先走 git。 platform-readings.md:137 「PR 文件读取同走 git:REST PR files 端点可瞬态 404。」 — the same fact and the same prescription, naming the endpoint.

platform-readings.md −1:

deleted survives at
- 同一路径吃掉标题里的尖括号跨度 ⇒ 标题占位写裸词(NAME / :id),⛔ 不写尖括号形。 The authoring rule survives verbatim in .claude/agents/os-dev.md 〈字节与 sanitizer 纪律〉 —「凡要上 GitHub 的文本,尖括号形状片段一律改占位词拼写(FIELDIDENT.MEMBER 一类)。」— and in AGENTS.md's GitHub mutates body BYTES paragraph. The mechanism survives at platform-readings.md:218 「读侧并非一律可逆:行内反引号里的尖括号片段被 MCP 读路径整个丢弃,非转义,无从解码。」, :220 (comments) and :224 (the pre-flight check).

⚠️ Declared openly rather than smuggled: that line's one non-restating clause — its attribution of the loss to the create write path — is the clause this flight measured false (objectui#8185 above: MCP-created, span present in storage). The surviving statements at :218 / :220 / :224 are the correct ones, so the fold improves the file rather than merely paying for a line. The remaining half — that the read-path loss truncates a title from the first such span to its end, which :218 does not say — is out of this card's scope and is recorded below.

Premises, as measured

premise verdict
No workflow in either repo shells an authored title holds — re-run at both origin/main tips
No scripts/** shell path creates issues holds
The likeliest path is an agent's own REST create from Bash confirmed on the specimen (footer intact ⇒ REST) and on 6 more
rest-channel.md has no ratchet row falsified — row exists at 82, file was at 82; fold-paid instead
search_issues cannot answer the population holds — 422 reproduced verbatim in both repos

Gates

Derived at the final HEAD, never hand-listed: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, whose stderr names the tree — gate list derived from the tree of 'objectstack-ai/objectstack' at commit cb99940975, change set 2 paths vs merge base 779710213. Every exit captured to a file before any pipe.

command exit
node scripts/check-closing-keyword-parity.mjs 0
node scripts/check-closing-keyword-parity.mjs --self-test 0
node scripts/check-comment-mask-corpus.mjs 0
node scripts/pm/check-governed-queue-guard.mjs --self-test 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions 3 → 0
pnpm check:agent-test-spelling 0
pnpm check:doc-authoring 0
pnpm check:driver-memory-census 0
pnpm check:nul-bytes 0
pnpm check:pm-governed-merges 0
pnpm check:pm-skill-id-lint 0
pnpm check:pm-skill-ratchet 0
pnpm check:refd-timer-probe 0
pnpm check:required-contexts 0
pnpm check:skill-frame-sync 0
pnpm check:watch-hint-literal 0
pnpm check:pm-governed-prose (dispatch-named, not derived) 0

The single exit 3 is PREREQUISITE NOT MET, not a finding — and it reproduced objectstack#15850 exactly: it named @objectstack/formula, and after that build landed it refused again for @objectstack/lint. Both builds ran under the shared lock (OS_VERIFY_LOCK_SLOT=issue-16661; VERDICT command-exit 0 · held the lock 118s · waited 0s, then 11s), after which the gate is 0.

Ratchet verdict lines, quoted from the gate's own output:

✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md is 395 lines (ceiling 395; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/rest-channel.md is 82 lines (ceiling 82; headroom 0).

Reconciliation: dispatch-gates --ran prints ✓ dispatch-gates --ran: 16 derived famil(ies) accounted for — 16 run, 0 NOT-MEASURED., with pnpm check:pm-governed-prose listed as the one run outside the derivation.

Governed classification: node scripts/pm/check-governed-merges.mjs --test on both paths ⇒ exit 3, ⛔ GOVERNED — a human merge is the review record for this PR (#9495 regime).

pnpm lint — a declared narrowing, with its three evidences

Population, read from eslint.config.mjs itself, not guessed. Every rule-bearing block's files glob is a TypeScript/JavaScript extension set — **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} (:971), packages/cli/src/**/*.{ts,tsx,mts,cts} (:1015), examples/** + packages/apps/** (:1054), packages/**/*.{ts,tsx,mts,cts} (:1103, :1172), **/*.{ts,tsx,mts,cts} (:1212), COMMENT_SWALLOW_FILES (:1238). No block names .md. This diff is markdown-only, so it is outside the linted population by construction.

Count, read from --format json, at final HEAD cb99940975. pnpm exec eslint --no-inline-config --format json over the two changed paths: 2 result entries, errorCount: 0, warningCount: 2, both warnings being eslint's own File ignored because no matching configuration was supplied. — the tool confirming ⓵ in its own words.

Invariance for untouched files. Type-aware linting is not enabled: every one of the 7 rule-bearing blocks carries parserOptions: { ecmaVersion: 'latest', sourceType: 'module' } and nothing else — no parserOptions.project, no projectService, no typed @typescript-eslint rules (the config says so itself at :328). No rule reads across a file boundary, so a markdown-only diff cannot move any untouched file's verdict.

All three are present, so this is a measurement, not an omission. The full repo-wide eslint . --no-inline-config run belongs to CI.

Mergeability

git fetch origin main && git merge-tree --write-tree --name-only origin/main HEAD at origin/main 61b4eb3a40 (it moved from the dispatch tip 7797102139 during the flight): exit 0, output is a bare tree oid 683be9fc5f7633dccacf424a9f82c668a386ebbf with no conflict file list. The deferred-judgement caveat does not apply here — git check-attr merge answers unspecified for both paths, so no merge driver was routed and this is a real text merge.

How this flight carried its own shell-bound strings

This card is about a string reaching a shell, so the flight held itself to the rule it is writing. Every string containing a backtick, $(, or a $VAR that this flight passed through Bash travelled in a single-quoted heredoc or a file, never an inline double-quoted string:

  • the two documentation edits were applied by a Python script whose patch text — three added lines carrying backticks, $(...) and $VAR, and three deletion targets carrying backticks — lives inside a <<'PYEOF' heredoc, and the script refuses unless each deletion target and each anchor matches exactly once;
  • the commit message went to git commit -F from a file written by a <<'MSGEOF' heredoc;
  • this PR body went to a file written by a <<'BODYEOF' heredoc;
  • the GitHub page fetches and the payload extraction ran from .sh / .mjs files, not from inline command strings.

The one deliberate exception is the credential: curl … -H "Authorization: Bearer $GH_TOKEN" expands a variable this flight owns, as an argv element, never inside a JSON payload. That is the distinction the new rule draws — the hazard is untrusted content inside double quotes, not a variable you meant to expand.

验收备注

  • The MCP read path truncates a title from its first tag-shaped span to the end — measured on objectstack#15180 (`objects., cut mid-token) and objectui#8185. platform-readings.md:218 records the class as 「整个丢弃」 (the span is discarded); truncation-to-end is a stronger behaviour it does not state, and it silently shortens every title-based population reading. Filed separately — 承接者: the card filed by this flight (linked from the report comment); ⛔ not folded in here, because :218 is at a zero-headroom ceiling and correcting it is not this card's line.
  • A footer-shaped block quoted inside a fenced code block in a PR body is stripped on store — measured on this very body: the first version reproduced objectui#7562's trailing attribution block as evidence inside a fence, and the read-back came back with the block gone and everything else byte-intact. The readings record footer stripping on issue create and footer doubling on PR create; a quoted footer being eaten from inside a fence is neither. 承接者: folded into the card this flight filed (linked from the report comment), same instrument family.
  • objectui#7235's stored body carries the attribution footer twice — noted, not filed: it is objectstack#14997's already-open class ("The attribution footer is appended on the CREATE path too — a hand-written one lands doubled"), and Fixes #7175: HookSchema.condition @example is SQL, not CEL #7235 is a fresh instance of it. 承接者: objectstack#14997.
  • The six new specimens' titles are recorded above verbatim and left unrepaired, per triage's ⛔ 不要顺手"修好"更多标题. 承接者: whoever picks up each card; the evidence is preserved here first, which is the order triage asked for.
  • AGENTS.md's commit-trailer rule and the harness's attribution default disagree about model identifiers. This commit used the model-free pair AGENTS.md prescribes. Noted, not filed — objectstack#14663 is already open on exactly this. 承接者: objectstack#14663.

维护者速读(草稿)

改了什么 — 两个 agent 指令文件各加了一条规则,共 3 行;为了不抬天花板,同时删掉 3 行重复内容(内容在别处都还在,逐条列在上面)。规则只有一句话:agent 用 curl 建 GitHub issue 时,请求体必须走文件或引号定界的 heredoc,不许拼在双引号字符串里。

为什么改 — 有人把 issue 标题里的反引号当成了 shell 命令来执行,而且是以 root 身份。这不是笔误:本轮在两个仓一周内的 1,750 条标题里,量到 7 条同样的痕迹(原样保留在上面,一条没改)。本车队写标题的习惯就是给标识符加反引号,所以这是一个会持续复发的口子。⛔ 要动的是引用方式,不是写作习惯 —— 让大家「别在标题里用反引号」是修错了那一侧。

风险与代价(含回滚) — 极低。改动只是两个 markdown 指令文件,不发布任何包,不动一行代码,也不新增任何门禁(门禁看不见 agent 自己的 shell)。全部 17 条门禁绿,本地试合并干净。回滚 = 直接 revert 这一个提交,没有任何下游依赖。

席位意见

你要做的 — 这是受管面(.claude/**),按规矩只能人工合并:没有任何 agent 会去点 ready、入队或开自动合并。请您看一眼上面那三行新规则的措辞是否准确,然后决定:合,还是不合?(是 / 否)


Generated by Claude Code

…quoted heredoc, never an inline double-quoted string

The filing path that command-substituted an issue title is an agent's own
REST create from Bash: the instruction surface prescribes `POST /issues`
without saying how the body travels, and a JSON body built inline in a
double-quoted string hands every backticked identifier in the title to the
shell before the request exists.

rest-channel.md gains the carriage rule and its signature on the write side;
platform-readings.md gains the measured fact. Both files are at their ratchet
ceiling, so both additions are fold-paid: two restatements leave rest-channel
(each survives in platform-readings, which that table's own rule says holds
them), and one restatement leaves platform-readings.

Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
Co-authored-by: Claude <noreply@anthropic.com>

os-musk commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

ACCEPT — flight P, PR #16701 at head cb99940975 (#16661 p1: the filing path located, the carriage rule on the instruction surface), contract-tier review on the seat's own readings, 2026-09-07T23:13Z.

Own readings (review worktree detached at cb99940975, origin/main = 61b4eb3a40, merge base 7797102139):

  • Diff: two files, +3/−3, one commit; rest-channel.md 82 lines and platform-readings.md 395 lines, both at their ratchet ceilings (the seat's brief said rest-channel.md had no ratchet row — it has one, at 82; the dev measured it and fold-paid that file too, which is the right response to a false premise); added lines measure 118, 98 and 118 bytes; no control bytes; git merge-tree --write-tree --name-only origin/main HEAD exit 0, tree 683be9fc5, no conflicted path.
  • Fold survivals read on the head: the list_issues/assignees fact stands at platform-readings.md:213 and the REST read row at rest-channel.md:24; the PR-files transient-404 rule stands at platform-readings.md:137; the angle-bracket placeholder rule stands at os-dev.md:319–321 and the read-path mechanism at platform-readings.md:218 and :275–280. The one clause the deleted :349 carried that survives nowhere — attributing the span loss to the create path — is the clause the flight measured false on objectui#8185 (MCP-created, span intact in storage), so the fold corrects the ledger rather than merely paying for a line.
  • Gates re-run here: check:pm-skill-ratchet (both files at ceiling, headroom 0), check:pm-skill-id-lint (26 files clean), check:skill-frame-sync, check:pm-governed-prose (2 surfaces name all 5 governed surfaces), all exit 0; check-governed-merges.mjs --test on both paths exit 3.
  • Path verdict: the seat's own locating grep (5576235369) and the flight's re-run agree — no workflow or script in either repo shells an authored title; the specimen's stored body keeps its attribution footer, which the ledger's own :348/:350 discriminator reads as REST create, and .claude/settings.json allowlists curl writes to the issues API. Agent-Bash-REST ⇒ instruction surface ⇒ routing stays domain:skills; triage's re-judgement trigger does not fire.
  • Population: 7 specimens in 1,750 titles over one week, both repos, with the five read-path false positives removed by a second channel — a lower bound, stated as such; objectstack#15511's gap token reconstructs exactly and cannot be a sanitizer artefact. The six new specimens are preserved verbatim and unrepaired, as triage asked.
  • CI at 23:11Z: 30 of 32 check runs completed success/skipped, Lint & Repo Gates in progress, and one Check Changeset run red — that run started at 23:04:12Z before skip-changeset was applied and the re-run at 23:07:08Z on the same head is skipped; a stale row, not a verdict on this diff. Not a landing condition on this surface.

Review checklist:

Implemented-by: os-dev subagent a8eef87c64a4adca9 (container & tier as recorded on the claim 5576266367 on #16661)
Reviewed-by: domain:skills seat, session session_018dxq7YqsLDMeZDZ5AzsgJX, at CONTRACT_REVIEW_TIER (own get_session reading: session_context.model = last_served_model)

维护者速读

改了什么: 两份 agent 指令文件共加三行、删三行,行数不动(rest-channel.md 82/82,platform-readings.md 395/395)。规则一句:agent 用 curl 建卡时请求体走文件或引号定界 heredoc,永不拼在双引号字符串里——双引号里 shell 先展开反引号、$(...)$VAR;只坏标题、正文完好就是这个形。删掉的三行内容都在别处完整存活,其中一行还纠正了账本把尖括号丢失记在写侧的旧说法。

为什么改: 有卡的标题里的反引号被当作命令以 root 执行了(uid=0(root))。路径已定位:不是任何 workflow 或脚本,是 agent 自己在 Bash 里内联 JSON 走 REST 建卡——本车队批准的写通道正是 curl。一周内两仓 1,750 条标题里量到 7 例,机制不罕见。修的是引用方式,不是「标题别用反引号」。

风险与代价(含回滚): 只动两份 markdown 指令,不发布任何包、不加门禁。回滚 = revert 一个提交。CI 里那条红的 Check Changeset 是贴 skip-changeset 标签前的旧跑,标签生效后的同名检查已 skipped。

席位意见: 复核通过,建议合并;由此衍生的读路径截断问题已另立 #16703,留给下一班。

你要做的: 手动合并本 PR(受管面,⛔ 不走队列、不挂 auto-merge);合并即关 #16661。是否合并?


Generated by Claude Code

os-musk commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

CI reading (seat, on the maintainer's 「16701 红了」, 2026-09-08T00:0xZ): the one red row is Check Changeset in run 34168773596 — the pull_request: opened run that started at 23:04:09Z, before skip-changeset was applied at ~23:07Z. Its own error text: 「This PR adds no changeset. If it releases nothing (including any 'skills/**' change …), apply the 'skip-changeset' label」. The two later runs of the same check on this same head (34168944431 at 23:07:08Z, 34169234963 at 23:12:18Z) read the label and are skipped; every other job on cb99940975 is green (Lint & Repo Gates success 23:28:51Z). Not a verdict on the diff — a stale pre-label run. The seat re-ran that run's failed job once (rerun_failed_jobs on 34168773596) so the row re-reads the label; no push, no empty commit. Governed terminal unchanged: draft, hand-merge.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 8, 2026 00:31
@os-zhuang
os-zhuang enabled auto-merge September 8, 2026 00:32
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit a916de6 Sep 8, 2026
40 of 41 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-16661-title-command-substitution branch September 8, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants